package cn.lczze.stock.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.lczze.stock.constant.Comments;
import cn.lczze.stock.constant.Constants;
import cn.lczze.stock.enums.FieldCodeEnum;
import cn.lczze.stock.enums.ProfitAndLossEnum;
import cn.lczze.stock.mapper.base.StockBaseMapper;
import cn.lczze.stock.mapper.base.StockInfoMapper;
import cn.lczze.stock.model.base.StockBaseModel;
import cn.lczze.stock.model.base.StockInfo;
import cn.lczze.stock.service.StockCalService;
import cn.lczze.stock.util.HttpUtils;
import cn.lczze.stock.util.StringUtils;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.assertj.core.util.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class StockCalServiceImpl implements StockCalService {


    @Autowired
    private StockBaseMapper stockBaseMapper;
    @Autowired
    private StockInfoMapper stockInfoMapper;
    @Resource
    private TaskExecutor taskExecutor;


    //获取所有股票当天日线数据
    public void getStockDayData(){
        JSONObject dateJson = this.getDateNum();
        String start_date = dateJson.get("yestDate").toString();
        String end_date = dateJson.get("currDate").toString();
        List<StockInfo> stockInfos = stockInfoMapper.selectAll();
        if (stockInfos == null || stockInfos.size() <= 0){
            log.error("未查到股票基础数据列表");
            return;
        }
        JSONObject stockApiDate = this.getStockApi(start_date, end_date);
        if (stockApiDate == null){
            log.error("获取 stock Api 异常");
            return;
        }
        this.updateStock(stockInfos, stockApiDate);
    }

    public void getStockDayDataCon(){
        JSONObject dateJson = this.getDateNum();
        String start_date = dateJson.get("yestDate").toString();
        String end_date = dateJson.get("currDate").toString();
        List<StockInfo> stockInfos = stockInfoMapper.selectAll();
        if (stockInfos == null || stockInfos.size() <= 0){
            log.error("未查到股票基础数据列表");
            return;
        }
        JSONObject stockApiDate = this.getStockApi(start_date, end_date);
        if (stockApiDate == null){
            log.error("获取 stock Api 异常");
            return;
        }
        this.updateStock(stockInfos, stockApiDate);
    }


    private JSONObject getStockApi(String start_date,String end_date){
        JSONObject request = new JSONObject();
        request.put("api_name", "daily");
        request.put("token","616be208fd54e2fa79c6517ed639689d18c03e4bd9f9b04bc95bf953");
        JSONObject params = new JSONObject();
        params.put("ts_code","");
        params.put("start_date", start_date);
        params.put("end_date", end_date);
        request.put("params", params);
        String fields = FieldCodeEnum.ts_code_1.getCode() + "," + FieldCodeEnum.trade_date_2.getCode() + "," +  FieldCodeEnum.open_3.getCode() + "," + FieldCodeEnum.high_4.getCode() + "," +
        FieldCodeEnum.low_5.getCode()+ "," + FieldCodeEnum.close_6.getCode() + "," + FieldCodeEnum.pre_close_7.getCode()+ "," + FieldCodeEnum.change_8.getCode() + "," +
        FieldCodeEnum.pct_chg_9.getCode() + "," +  FieldCodeEnum.vol_10.getCode() + "," +  FieldCodeEnum.amount_11.getCode();
        request.put("fields", fields);
        log.info("requestParam: {}", request);
        JSONObject result = HttpUtils.postBody("http://api.tushare.pro", 20, request, JSONObject.class);
//        log.info("{}",result);
        return result;
    }

    private void updateStock(List<StockInfo> stockInfos, JSONObject stockApiDate){

        JSONObject data = stockApiDate.getJSONObject("data");
        if (data == null){
            log.error("数据解析错误 data:{}", data);
            return;
        }
        JSONArray items = data.getJSONArray("items");
        if (items == null){
            log.error("数据解析错误 items:{}", items);
        }
        Map<String, Object> stockDateMap = new HashMap<>();
        for (Object item : items) {
            ArrayList arrayList = JSONObject.parseObject(JSONObject.toJSONString(item), ArrayList.class);
            Object ts_code_1 =  arrayList.get(FieldCodeEnum.ts_code_1.getSort() -1);
            stockDateMap.put(ts_code_1.toString(), item);
        }
        for (StockInfo stockInfo : stockInfos) {
            String tsCode = stockInfo.getTsCode();
            if (tsCode == null){
                log.error("tsCode 为空 {}", JSONObject.toJSONString(stockInfo));
                continue;
            }
            this.setStockData(stockDateMap, stockInfo);
        }
    }

    private void setStockData(Map<String, Object> stockDateMap, StockInfo stockInfo){
        Object ApiDate = null;
        Object ts_code_1 = null;
        Object trade_date_2 = null;
        Object open_3 = null;
        Object high_4 = null;
        Object low_5 = null;
        Object close_6 = null;
        Object pre_close_7 = null;
        Object change_8 = null;
        Object pct_chg_9 = null;
        Object vol_10 = null;
        Object amount_11 = null;
        try{
            ApiDate = stockDateMap.get(stockInfo.getTsCode());
            if (ApiDate == null){
                log.info("未找到该股票代码数据，{}", JSONObject.toJSONString(stockInfo.getName()));
                return;
            }
            ArrayList arrayList = JSONObject.parseObject(JSONObject.toJSONString(ApiDate), ArrayList.class);
            ts_code_1 =  arrayList.get(FieldCodeEnum.ts_code_1.getSort() -1);
            trade_date_2 =  arrayList.get(FieldCodeEnum.trade_date_2.getSort() -1);
            open_3 =  arrayList.get(FieldCodeEnum.open_3.getSort() -1);
            high_4 =  arrayList.get(FieldCodeEnum.high_4.getSort() -1);
            low_5 =  arrayList.get(FieldCodeEnum.low_5.getSort() -1);
            close_6 =  arrayList.get(FieldCodeEnum.close_6.getSort() -1);
            pre_close_7 =  arrayList.get(FieldCodeEnum.pre_close_7.getSort() -1);
            change_8 =  arrayList.get(FieldCodeEnum.change_8.getSort() -1);
            pct_chg_9 =  arrayList.get(FieldCodeEnum.pct_chg_9.getSort() -1);
            vol_10 =  arrayList.get(FieldCodeEnum.vol_10.getSort() -1);
            amount_11 =  arrayList.get(FieldCodeEnum.amount_11.getSort() -1);
        }catch (Exception e){
            log.error("解析数据错误：{}", ApiDate);
            return;
        }
        StockInfo stockInfoInsert = new StockInfo();
        stockInfoInsert.setId(stockInfo.getId());
        Double price = Double.valueOf(close_6.toString());
        Float pct_chg_rate = Float.valueOf(pct_chg_9.toString());
        stockInfoInsert.setPrice(price);
        stockInfoInsert.setUpdateTime(new Date());

        String stockLimit = this.checkStockLimit(stockInfo.getPrice(), price, stockInfo.getMarket());
        stockInfoInsert.setContinueDay(0);
//        if (stockLimit == null){
//            return;
//        }
        //变化率大于0 视为增长
        stockInfoInsert.setContinueDay(0);
        if (pct_chg_rate > 0){
            if (Comments.UP_LIMIT.equals(stockLimit)){
                //当天是涨停的股票，前一天是跌停，需要设置 continueDay = 0
                if (ProfitAndLossEnum.LOSS.getCode().equals(stockInfo.getProfitAndLoss())){
                    stockInfo.setContinueDay(0);
                }
                stockInfoInsert.setContinueDay(stockInfo.getContinueDay() + 1);
            }
            stockInfoInsert.setProfitAndLoss(ProfitAndLossEnum.PROFIT.getCode());
        }else {
            if (Comments.DOWN_LIMIT.equals(stockLimit)){
                if (ProfitAndLossEnum.PROFIT.getCode().equals(stockInfo.getProfitAndLoss())){
                    stockInfo.setContinueDay(0);
                }
                stockInfoInsert.setContinueDay(stockInfo.getContinueDay() + 1);
            }
            stockInfoInsert.setProfitAndLoss(ProfitAndLossEnum.LOSS.getCode());
        }
//        insertData.setRemark(JSONObject.toJSONString(ApiDate));
        this.setRemark(stockInfoInsert, ts_code_1,  trade_date_2,  open_3,  high_4, low_5,
                 close_6,  pre_close_7,  change_8,  pct_chg_9,  vol_10,  amount_11);
        stockInfoInsert.setId(IdUtil.fastUUID());
        stockInfoInsert.setUpdateDate(new Date());

        int i = stockInfoMapper.updateStockInfoByTsCode(stockInfoInsert);
    }

    private void setRemark(StockInfo model, Object ts_code_1, Object trade_date_2, Object open_3, Object high_4,Object low_5,
                           Object close_6, Object pre_close_7, Object change_8, Object pct_chg_9, Object vol_10, Object amount_11){

        model.setTsCode(String.valueOf(ts_code_1));
//        model.setTrade_date_2(trade_date_2);
        model.setOpen(open_3.toString());
        model.setHigh(high_4.toString());
        model.setLow(low_5.toString());
        model.setChange(change_8.toString());
        model.setPctChg(pct_chg_9.toString());
        model.setVol(vol_10.toString());
        model.setAmount(amount_11.toString());
    }





    public JSONObject getDateNum(){
        JSONObject jsonObject = new JSONObject();
        // 获取当前日期
        LocalDate today = LocalDate.now();
        // 获取前一天的日期
        LocalDate yesterday = today.minusDays(1);

        // 定义输出格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");

        // 输出当天和前一天的日期
        System.out.println("今天的日期: " + today.format(formatter));
        System.out.println("昨天的日期: " + yesterday.format(formatter));
        jsonObject.put("currDate", today.format(formatter));
        jsonObject.put("yestDate", yesterday.format(formatter));
//        jsonObject.put("yestDate", "20250416");
//        jsonObject.put("currDate", "20250417");


        DateTimeFormatter formatterYear = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
        jsonObject.put("currDateYear", today.format(formatterYear));
        jsonObject.put("yestDateYear", yesterday.format(formatterYear));
        return jsonObject;
    }


    // 市场类型与涨跌幅映射
    private static final Map<String, BigDecimal> MARKET_RATES = new HashMap<>();
    private static final BigDecimal EPSILON = new BigDecimal("0.002");

    static {
        // 初始化市场规则（最新监管要求）
        MARKET_RATES.put("主板", new BigDecimal("0.10"));    // 10%
        MARKET_RATES.put("创业板", new BigDecimal("0.20"));   // 20%
        MARKET_RATES.put("科创板", new BigDecimal("0.20"));   // 20%
        MARKET_RATES.put("北交所", new BigDecimal("0.30"));    // 30%
        MARKET_RATES.put("CDR", new BigDecimal("0.10"));      // 10%
    }

    public static String checkStockLimit(Double prevPrice,
                                         Double currentPrice,
                                         String market) {
        if (prevPrice == null || currentPrice == null){
            log.error("prevPrice 或 currentPrice 是空");
            return null;
        }
        // 验证市场类型有效性
        if (!MARKET_RATES.containsKey(market)) {
            throw new IllegalArgumentException("不支持的市場类型: " + market);
        }

        BigDecimal rate = MARKET_RATES.get(market);
        BigDecimal prev = BigDecimal.valueOf(prevPrice);

        // 当前价格处理（三级精度）
        BigDecimal current = BigDecimal.valueOf(currentPrice)
                .setScale(3, RoundingMode.HALF_UP)
                .setScale(2, RoundingMode.HALF_UP);

        // 计算涨跌停价（保留三位中间精度）
        BigDecimal upperLimit = prev.multiply(BigDecimal.ONE.add(rate))
                .setScale(3, RoundingMode.HALF_UP)
                .setScale(2, RoundingMode.HALF_UP);

        BigDecimal lowerLimit = prev.multiply(BigDecimal.ONE.subtract(rate))
                .setScale(3, RoundingMode.HALF_UP)
                .setScale(2, RoundingMode.HALF_UP);

        // 带容差比较
        if (compareWithTolerance(current, upperLimit)) {
            return Comments.UP_LIMIT;
        } else if (compareWithTolerance(current, lowerLimit)) {
            return Comments.DOWN_LIMIT;
        } else {
            return Comments.COMMON;
        }
    }

    private static boolean compareWithTolerance(BigDecimal a, BigDecimal b) {
        return a.subtract(b).abs().compareTo(EPSILON) <= 0;
    }

    @Override
    public String getStockAnalysis() {
        List<StockInfo> stockInfos = stockInfoMapper.selectAll();
        if (stockInfos == null )

        stockInfos = stockInfos.stream().filter(p -> p.getContinueDay() > 0).collect(Collectors.toList());
        Map<Integer, List<StockInfo>> profitContinueDayMap = new HashMap<>();
        Map<Integer, List<StockInfo>> lossContinueDayMap = new HashMap<>();
        Integer profitStockSize = 0;
        Integer lossStockSize = 0;
        for (StockInfo stockInfoModel : stockInfos) {
            Integer continueDay = stockInfoModel.getContinueDay();
            if (stockInfoModel.getProfitAndLoss() == null || continueDay == null){
                continue;
            }
            if (continueDay == 0){
                continue;
            }
            if (stockInfoModel.getProfitAndLoss().equals(ProfitAndLossEnum.PROFIT.getCode())){
                this.getContinueDayMap(profitContinueDayMap, stockInfoModel);
                profitStockSize ++;
            }else if (stockInfoModel.getProfitAndLoss().equals(ProfitAndLossEnum.LOSS.getCode())){
                this.getContinueDayMap(lossContinueDayMap, stockInfoModel);
                lossStockSize ++;
            }
        }
        Set<Integer> profitContinueDaySet = profitContinueDayMap.keySet();
        List<Integer> profitContinueDayList = profitContinueDaySet.stream().sorted().collect(Collectors.toList());

        StringBuilder profitMessage = new StringBuilder();
        JSONObject dateNum = this.getDateNum();
        String currDateYear = dateNum.getString("currDateYear");
        profitMessage.append(currDateYear + "今日共有" + profitStockSize + "只股票涨停，有" + lossStockSize + "只股票跌停（不含ST股），其中涨停连板:");
        for (Integer day : profitContinueDayList) {
            List<StockInfo> infos = profitContinueDayMap.get(day);
            if (day <= 1 ){
                continue;
            }
            if (StringUtils.isNotEmpty(profitMessage.toString())){
                profitMessage.append(";");
            }
            profitMessage.append(day + "板：");
            int size = infos.size();
            int index = 1;
            for (StockInfo info : infos) {
                profitMessage.append(info.getName());
                if (index < size){
                    profitMessage.append(",");
                }
                index++;
            }
        }



        Set<Integer> lossContinueDaySet = lossContinueDayMap.keySet();
        List<Integer> lossContinueDayList = lossContinueDaySet.stream().sorted().collect(Collectors.toList());
        profitMessage.append("。其中跌停连板：");

        StringBuilder lossMessage = new StringBuilder();
        for (Integer day : lossContinueDayList) {
            List<StockInfo> infos = lossContinueDayMap.get(day);
            if (day <= 0){
                continue;
            }
//            if (StringUtils.isNotBlank(lossMessage.toString())){
//                lossMessage.append(";");
//            }
            lossMessage.append(";" + day + "板：");
            int size = infos.size();
            int index = 1;
            for (StockInfo info : infos) {
                lossMessage.append(info.getName());
                if (index < size){
                    lossMessage.append(",");
                }
                index++;
            }
        }
        profitMessage.append(lossMessage.toString());



        return profitMessage.toString();
    }

    private void getContinueDayMap(Map<Integer, List<StockInfo>> profitContinueDayMap, StockInfo stockInfoModel){
        if (profitContinueDayMap.containsKey(stockInfoModel.getContinueDay())) {
            List<StockInfo> stockBaseModelDayList = profitContinueDayMap.get(stockInfoModel.getContinueDay());
            stockBaseModelDayList.add(stockInfoModel);
            profitContinueDayMap.put(stockInfoModel.getContinueDay(), stockBaseModelDayList);
        }else {
            profitContinueDayMap.put(stockInfoModel.getContinueDay(), Lists.newArrayList(stockInfoModel));
        }
    }

    private String currDate;
    private String yestDate;

    @Override
    public void calMany() {


    }
}
